import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:collection/collection.dart';
import 'package:riki_router/core/riki_route.dart';

import 'riki_page.dart';
import 'riki_route_helper.dart';

/// [RikiRouterDelegate.navigatorWrapper]
typedef NavigatorWrapper = Widget Function(Navigator navigator);

///  [RikiRouterDelegate.popUntil] 使用的判断方式
typedef PagePredicate = bool Function(RikiPage<dynamic> page);

/// 一个由[Router]小部件用来构建和配置导航小部件的委托。
class RikiRouterDelegate extends RouterDelegate<RouteSettings> with ChangeNotifier, PopNavigatorRouterDelegateMixin<RouteSettings> {
  RikiRouterDelegate({
    required this.getRouteSettings,
    //this.reportsRouteUpdateToEngine,
    this.onPopPage,
    this.navigatorWrapper,
    this.observers,
    this.pageWrapper,
    this.transitionDelegate = const DefaultTransitionDelegate<dynamic>(),
  }) : navigatorKey = GlobalKey<NavigatorState>();

  /// 用于决定路由如何在屏幕内或外转换的委托
  ///
  /// 如果没有指定，默认为[DefaultTransitionDelegate]，不能为空。
  final TransitionDelegate<dynamic> transitionDelegate;

  /// 当最上面的路由发生变化时，该导航器是否应该向引擎报告路由更新消息。
  final PopPageCallback? onPopPage;

  /// Navigator的包装
  final NavigatorWrapper? navigatorWrapper;

  /// Page的包装器，您可以在这个回调中重新定义Page
  final PageWrapper? pageWrapper;

  /// [riki_router_library] 生成的方法
  final GetRouteSettings getRouteSettings;

  /// 此导航器的观察者列表。
  final List<NavigatorObserver>? observers;

  final List<RikiPage<dynamic>> _pages = <RikiPage<dynamic>>[];

  /// 导航器的当前所有页面
  List<RikiPage<dynamic>> get pages => _pages;

  /// 用于检索当前导航器的键
  @override
  final GlobalKey<NavigatorState> navigatorKey;

  NavigatorState? get navigatorState => navigatorKey.currentState;

  /// 从给定的上下文中检索[RouterDelegate]祖先。
  static RikiRouterDelegate of(BuildContext context) {
    final RouterDelegate<dynamic> delegate = Router.of(context).routerDelegate;
    assert(delegate is RikiRouterDelegate, 'Delegate type must match');
    return delegate as RikiRouterDelegate;
  }

  @override
  Future<bool> popRoute() async {
    return super.popRoute();
  }

  @override
  Widget build(BuildContext context) {
    final Navigator navigator = Navigator(
      pages: pages.toList(),
      key: navigatorKey,
      transitionDelegate: transitionDelegate,
      reportsRouteUpdateToEngine: false,
      onPopPage: onPopPage ??
          (Route<dynamic> route, dynamic result) {
            if (_pages.length > 1 && route.settings is RikiPage) {
              final RikiPage<dynamic>? removed = _pages.lastWhereIndexedOrNull(
                (int index, RikiPage<dynamic> element) => element.name == route.settings.name,
              );
              if (removed != null) {
                _pages.remove(removed);
                updatePages();
              }
            }
            return route.didPop(result);
          },
      observers: <NavigatorObserver>[HeroController(), if (observers != null) ...observers!],
    );

    if (navigatorWrapper != null) {
      return navigatorWrapper!(navigator);
    } else {
      return navigator;
    }
  }

  @override
  Future<void> setNewRoutePath(RouteSettings configuration) {
    _pages.add(getRoutePage(name: configuration.name!, arguments: configuration.arguments as Map<String, dynamic>?));
    return SynchronousFuture<void>(null);
  }

  @override
  RouteSettings? get currentConfiguration => _pages.isNotEmpty ? _pages.last : null;

  RikiPage<T?> getRoutePage<T extends Object?>({
    required String name,
    Map<String, dynamic>? arguments,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    PageRouteType? pageRouteType,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    RikiPage<T?> ffPage = getRouteSettings(name: name, arguments: arguments).toRikiPage<T>(
      key: getUniqueKey(),
      transitionMilliseconds: transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds,
      pageRouteType: pageRouteType,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
    if (pageWrapper != null) {
      ffPage = pageWrapper!(ffPage);
    }
    return ffPage;
  }

  /// 基于页面查询[RikiPage]
  RikiPage<dynamic>? find(Widget page) {
    return pages.firstWhereOrNull((element) => element.widget == page);
  }

  /// 基于页面查询[RikiPage]的[heroKeys]
  List<String> findHeroKeys(Widget page) {
    return find(page)?.heroKeys ?? <String>[];
  }

  /// 是否可以进行pop操作
  bool? canPop() => navigatorKey.currentState?.canPop();

  /// pop操作
  void pop<T extends Object?>([T? result]) {
    navigatorState!.pop<T>(result);
  }

  /// 判断是否可进行pop操作后，执行pop
  Future<bool> maybePop<T extends Object?>([T? result]) {
    return navigatorState!.maybePop<T>(result);
  }

  /// pop当前页面并push新页面
  Future<T?> popAndPushNamed<T extends Object?>(
    String routeName, {
    T? result,
    Map<String, dynamic>? arguments,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    PageRouteType? pageRouteType,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    pop<T>(result);
    return pushNamed<T>(
      routeName,
      arguments: arguments,
      transitionMilliseconds: transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds,
      pageRouteType: pageRouteType,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
  }

  /// pop当前页面并push新页面
  Future<T?> popAndPushUri<T extends Object?>(
    String uri, {
    T? result,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    PageRouteType? pageRouteType,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    Uri pageUri = Uri.parse(uri);
    assert(pageUri.path != '', '路由解析失败');
    return popAndPushNamed(
      pageUri.path,
      result: result,
      arguments: pageUri.queryParameters,
      transitionMilliseconds: transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds,
      pageRouteType: pageRouteType,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
  }

  /// 根据name多级返回
  void popUntilWithNamed(
    String name, {
    bool remove = true,
    bool removeCondition = false,
  }) {
    popUntil((page) => page.name == name, remove: remove, removeCondition: removeCondition);
  }

  /// 根据RikiPage多级返回
  void popUntilWithPage(
    RikiPage popPage, {
    bool remove = true,
    bool removeCondition = false,
  }) {
    popUntil((page) => page == popPage, remove: remove, removeCondition: removeCondition);
  }

  /// 多级返回页面
  void popUntil(
    PagePredicate predicate, {
    bool remove = true,
    bool removeCondition = false,
  }) {
    _popUntil(predicate, remove: remove, removeCondition: removeCondition);
    updatePages();
  }

  /// 多级返回实际执行方案
  /// 如进行了全部栈移除，则会保留路由栈中的第一个
  void _popUntil(
    PagePredicate predicate, {
    bool remove = true,
    bool removeCondition = false,
  }) {
    // 查询是否存在popUntil条件页面
    RikiPage<dynamic>? page = pages.lastWhereOrNull((element) => predicate(element));
    if (page == null) {
      // 未查询到
      // 开启remove时，则返回路由首页
      if (remove && pages.isNotEmpty) {
        pages.removeRange(1, pages.length);
      }
    } else {
      // 查询到
      // 剔除路由中间栈
      final List<RikiPage<dynamic>> removed = <RikiPage<dynamic>>[];
      for (int i = pages.length - 1; i >= 0; i--) {
        final RikiPage<dynamic> page = pages[i];
        if (predicate(page)) {
          if (removeCondition) {
            removed.add(page);
          }
          break;
        }
        removed.add(page);
      }
      if (removed.length == pages.length) {
        pages.removeRange(1, pages.length);
      } else {
        pages.removeWhere((RikiPage<dynamic> element) => removed.contains(element));
      }
    }
  }

  /// push新页面
  Future<T?> pushNamed<T extends Object?>(
    String routeName, {
    Map<String, dynamic>? arguments,
    bool replace = false,
    bool clearStack = false,
    List<String>? heroKeys,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    PageRouteType? pageRouteType,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    final RikiPage<T?> page = getRoutePage<T>(
      name: routeName,
      arguments: arguments,
      transitionMilliseconds: transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds,
      pageRouteType: pageRouteType,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
    page.heroKeys.addAll(heroKeys ?? <String>[]);
    if (replace) {
      pages.removeLast();
    }
    if (clearStack) {
      pages.clear();
    }
    pages.add(page);
    updatePages();
    return page.popped;
  }

  /// push新页面
  Future<T?> push<T extends Object?>(
    RikiPage<T?> page, {
    bool replace = false,
    bool clearStack = false,
    required List<String>? heroKeys,
  }) {
    page.heroKeys.addAll(heroKeys ?? <String>[]);
    if (replace) {
      pages.removeLast();
    }
    if (clearStack) {
      pages.clear();
    }
    pages.add(page);
    updatePages();
    return page.popped;
  }

  /// push新页面
  /// [uri] 链接方式跳转
  Future<T?> pushUri<T extends Object?>(
    String uri, {
    bool replace = false,
    bool clearStack = false,
    List<String>? heroKeys,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    PageRouteType? pageRouteType,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    Uri pageUri = Uri.parse(uri);
    assert(pageUri.path != '', '路由解析失败');
    return pushNamed(
      pageUri.path,
      arguments: pageUri.queryParameters,
      replace: replace,
      clearStack: clearStack,
      heroKeys: heroKeys,
      transitionMilliseconds: transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds,
      pageRouteType: pageRouteType,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
  }

  /// push新页面，并移除条件满足之间的页面
  Future<T?> pushAndRemoveUntil<T extends Object?>(
    RikiPage<T?> newPage,
    PagePredicate predicate, {
    bool remove = true,
    bool removeCondition = false,
    List<String>? heroKeys,
  }) {
    _popUntil(predicate, remove: remove, removeCondition: removeCondition);
    return push<T>(
      newPage,
      heroKeys: heroKeys,
    );
  }

  /// push新页面，并移除removeRouteName之间的页面
  Future<T?> pushNamedAndRemoveUntilWithNamed<T extends Object?>(
    String newRouteName,
    String removeRouteName, {
    Map<String, dynamic>? arguments,
    bool remove = true,
    bool removeCondition = false,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    PageRouteType? pageRouteType,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    return pushNamedAndRemoveUntil(
      newRouteName,
      (page) => page.name == removeRouteName,
      arguments: arguments,
      remove: remove,
      removeCondition: removeCondition,
      transitionMilliseconds: transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds,
      pageRouteType: pageRouteType,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
  }

  /// push新页面，并移除removePage之间的页面
  Future<T?> pushNamedAndRemoveUntilWithPage<T extends Object?>(
    String newRouteName,
    RikiPage removePage, {
    Map<String, dynamic>? arguments,
    bool remove = true,
    bool removeCondition = false,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    PageRouteType? pageRouteType,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    return pushNamedAndRemoveUntil(newRouteName, (page) => page == removePage,
        arguments: arguments,
        remove: remove,
        removeCondition: removeCondition,
        transitionMilliseconds: transitionMilliseconds,
        reverseTransitionMilliseconds: reverseTransitionMilliseconds,
        pageRouteType: pageRouteType,
        routeTransitionsBuilder: routeTransitionsBuilder);
  }

  /// push新页面，并移除条件满足之间的页面
  Future<T?> pushNamedAndRemoveUntil<T extends Object?>(
    String newRouteName,
    PagePredicate predicate, {
    Map<String, dynamic>? arguments,
    bool remove = true,
    bool removeCondition = false,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    PageRouteType? pageRouteType,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    _popUntil(predicate, remove: remove, removeCondition: removeCondition);
    return pushNamed<T>(
      newRouteName,
      arguments: arguments,
      transitionMilliseconds: transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds,
      pageRouteType: pageRouteType,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
  }

  /// push新页面，并移除removeRouteName之间的页面
  Future<T?> pushUriAndRemoveUntilWithNamed<T extends Object?>(
    String newRouteName,
    String removeRouteName, {
    bool remove = true,
    bool removeCondition = false,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    PageRouteType? pageRouteType,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    return pushUriAndRemoveUntil(
      newRouteName,
      (page) => page.name == removeRouteName,
      remove: remove,
      removeCondition: removeCondition,
      transitionMilliseconds: transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds,
      pageRouteType: pageRouteType,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
  }

  /// push新页面，并移除removePage之间的页面
  Future<T?> pushUriAndRemoveUntilWithPage<T extends Object?>(
    String newRouteName,
    RikiPage removePage, {
    bool remove = true,
    bool removeCondition = false,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    PageRouteType? pageRouteType,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    return pushUriAndRemoveUntil(
      newRouteName,
      (page) => page == removePage,
      remove: remove,
      removeCondition: removeCondition,
      transitionMilliseconds: transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds,
      pageRouteType: pageRouteType,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
  }

  /// push新页面，并移除条件满足之间的页面
  Future<T?> pushUriAndRemoveUntil<T extends Object?>(
    String uri,
    PagePredicate predicate, {
    bool remove = true,
    bool removeCondition = false,
    int? transitionMilliseconds,
    int? reverseTransitionMilliseconds,
    PageRouteType? pageRouteType,
    RouteTransitionsBuilder? routeTransitionsBuilder,
  }) {
    Uri pageUri = Uri.parse(uri);
    assert(pageUri.path != '', '路由解析失败');
    return pushNamedAndRemoveUntil(
      pageUri.path,
      predicate,
      arguments: pageUri.queryParameters,
      remove: remove,
      removeCondition: removeCondition,
      transitionMilliseconds: transitionMilliseconds,
      reverseTransitionMilliseconds: reverseTransitionMilliseconds,
      pageRouteType: pageRouteType,
      routeTransitionsBuilder: routeTransitionsBuilder,
    );
  }

  /// 跟新页面操作
  void updatePages() {
    _debugCheckDuplicatedPageKeys();
    notifyListeners();
  }

  void _debugCheckDuplicatedPageKeys() {
    assert(() {
      final Set<Key> keyReservation = <Key>{};
      for (final Page<dynamic> page in pages) {
        if (page.key != null) {
          assert(!keyReservation.contains(page.key));
          keyReservation.add(page.key!);
        }
      }
      return true;
    }());
  }

  /// 页面的Unique Key
  UniqueKey getUniqueKey() => UniqueKey();
}
